home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / src / exampleCode / audio / harmonizer / harmonizer.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-02  |  8.9 KB  |  313 lines

  1. /*
  2.  * Copyright (C) 1994, Silicon Graphics, Inc.
  3.  * All Rights Reserved.
  4.  *
  5.  * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
  6.  * the contents of this file may not be disclosed to third parties, copied or
  7.  * duplicated in any form, in whole or in part, without the prior written
  8.  * permission of Silicon Graphics, Inc.
  9.  *
  10.  * RESTRICTED RIGHTS LEGEND:
  11.  * Use, duplication or disclosure by the Government is subject to restrictions
  12.  * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
  13.  * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
  14.  * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
  15.  * rights reserved under the Copyright Laws of the United States.
  16.  */
  17. /* 
  18. Title: Simple harmonizer - a real-time audio pitch bender example program
  19.  
  20. Program: harmonizer.c
  21.  
  22. Author:  Dan Fink
  23.  
  24. Compile: cc harmonizer.c -o harmonizer -laudio -laudiofile -laudioutil -lm
  25.  
  26. Usage:    harmonizer note1 <note2 note3 ... >
  27.  
  28.     Output is to the standard Indigo audio device (speaker).
  29.  
  30.     Notes are specified by an integer relating
  31.     the note in half steps to middle C.  
  32.  
  33.     Middle C would be 0. C# would be 1
  34.  
  35.     for no effect at all     :      harmonizer 0
  36.     for the chipmunk effect  :     harmonizer 12
  37.     for a deep voice machine :    harmonizer -7
  38.     to simulate a major cord :     harmonizer 0 4 7
  39.  
  40. Comments: 
  41.  
  42.     Advise running as root or suid root to take advantage of
  43.     memory pinning and non-degrading high priority
  44.     of audio process.
  45.  
  46.     Libraries you need to compile with come from the Digital
  47.     Media Development option, now up to version 1.1 (for 
  48.     systems running IRIX 4.0.X) and version 1.2 (for systems
  49.     running IRIX 5.1)
  50.  
  51.     Pitch shifting of realtime input from the microphone.
  52.  
  53.     Code isn't optimized. 
  54.  
  55.     With the computing power of the indigo R3k, about 4 notes
  56.     can be harmonized simultaneously without a problem.
  57.     R4k does well up to even 8 notes.
  58.  
  59. */
  60.  
  61. #include <stdio.h>
  62. #include <limits.h>
  63. #include <stdlib.h>
  64. #include <sys/types.h>
  65. #include <sys/prctl.h>
  66. #include <sys/schedctl.h>
  67. #include <sys/prctl.h>
  68. #include <sys/lock.h>
  69. #include <signal.h>
  70. #include <audio.h>
  71. #include <audiofile.h>
  72. #include <math.h>
  73. #include <malloc.h>
  74.  
  75.  
  76. /* AUDIO DEFINES */
  77. #define BUFFERSIZE 1500            /* Size of output sample buffer. */
  78. #define PI 3.141592653             /* Our old friend from trig.     */
  79. #define TWELFTH_ROOT_OF_2 1.0594631
  80. #define MAXKEYS 8
  81. #define MIDDLEC 72
  82.  
  83.  
  84. /* AUDIO ROUTINES */
  85. void audioinit();
  86. void audioloop(void *);
  87. void process_continuous_audio( short *, short *, long);
  88. void audioexit(int);
  89.  
  90.  
  91. /* AUDIO RELATED GLOBALS */
  92. ALport         audioout;
  93. ALport         audioin;
  94. ALconfig     audioconfig;
  95. int         audiodone = 0;
  96.  
  97. /* OTHER GLOBALS */
  98. short key[MAXKEYS];
  99. int     done = 0;
  100.  
  101.  
  102. void sigcat()  /* for proper audio exit so a larger program's structure is 
  103.         easily divisible into master process and audio process */
  104. {
  105.     done = 1;
  106. }
  107.  
  108.  
  109. /* AUDIO ROUTINES */
  110.  
  111. void audioinit() /* Configure the audio port */
  112. {
  113.     audioconfig = ALnewconfig();             /* Get a new audio configuration */    
  114.  
  115. /*
  116.     ALsetsampfmt( audioconfig, AL_SAMPFMT_FLOAT); 
  117.     ALsetfloatmax(audioconfig, 1.0);      
  118. */
  119.                     /* Floats will vary from -1.0 to 1.0*/
  120.  
  121.  
  122.     ALsetqueuesize(audioconfig,BUFFERSIZE*2); 
  123.             /* Set the sample queue size to be twice that of our sound buffer */
  124.  
  125.     ALsetchannels(audioconfig,AL_MONO);             /* Use mono */
  126.  
  127.     audioout = ALopenport("out","w",audioconfig); 
  128.     if (audioout == NULL) {
  129.         printf("couldn't open audio port for output\n");
  130.         audioexit(-1);
  131.     }
  132.  
  133.     audioin = ALopenport("in","r",audioconfig); /* Open audio for writing */
  134.     if (audioin == NULL) {
  135.         printf("couldn't open audio port for input\n");
  136.         audioexit(-1);
  137.     }
  138. }
  139.  
  140.  
  141. void audioloop(void *foo)        /* Audio process main loop */
  142. {
  143.     short outbuf[BUFFERSIZE];         /* Sample buffer for computations */
  144.     short inbuf[BUFFERSIZE];         /* Sample buffer for computations */
  145.  
  146.     if(schedctl(NDPRI,0,NDPHIMAX) == -1)
  147.       printf("Couldn't run at high, non-degrading priority\n.");
  148.                                /* Make this process run at a high, non-       */
  149.                                /* degrading priority. This help significantly */
  150.                                /* "smooth out" audio clicking caused by losing*/
  151.                                /* the CPU to other processes.  Works best when*/
  152.                                /* the effective user id is the root user.     */
  153.  
  154.     if(plock(PROCLOCK)!=0)
  155.       printf("Couldn't lock process into memory\n.");
  156.                                /* Lock this process into memory - make it */
  157.                                /* immune to page swapping. Again, this is */
  158.                                /* another aid in preventing clicking which*/
  159.                                /* works when the effective user id is the */
  160.                                /* root user.                              */
  161.  
  162.     audioinit();               /* Initialize audio */
  163.     while(!done) {             /* Keep going until gfx process says otherwise */
  164.  
  165.     ALreadsamps(audioin,inbuf,BUFFERSIZE);
  166.  
  167.         process_continuous_audio(inbuf,outbuf,BUFFERSIZE);
  168.  
  169.         while(ALgetfillable(audioout) < BUFFERSIZE)
  170.             sginap(1);  
  171.             /* Wait til theres enough room to write the entire */
  172.                         /* copy of our sound buffer to the audio queue.    */
  173.  
  174.         ALwritesamps(audioout,outbuf,BUFFERSIZE); 
  175.                         /* Output samples to audio port */
  176.     }
  177.     
  178.     while(ALgetfilled(audioout) > 0);
  179.         /* Wait till all the sound has been played before closing the port.  */
  180.  
  181.     audioexit(0);
  182. }
  183.  
  184.  
  185. void process_continuous_audio(short *inbuf, short *outbuf, long buflen) 
  186. {
  187. static float index[MAXKEYS];
  188. static int firsttime = 1;
  189.  
  190. float maxkey = (float)MAXKEYS;
  191. float ratio;
  192. int i,j;
  193.  
  194. /*         
  195.         Harmonizer algorithm :
  196.  
  197.         Assume that the original pitch that we're bending
  198.         has its orginal pitch at MIDDLEC.  This is
  199.         an arbitrary starting point and could be anything. 
  200.  
  201.         To halve the pitch (move it down an octave) we 
  202.         duplicate samples.  AS expected, only half of the 
  203.         input buffer is used as it  gets copied to the audio 
  204.         output buffer. 
  205.  
  206.         To double the pitch (move it up an octave) we 
  207.         just copy every other sample of the original sound 
  208.         into the audio output buffer.
  209.  
  210.         Both of these are handled by computing the ratio
  211.         of the original pitch to the desired output pitch.
  212.         And skipping/repeating samples accordingly.
  213.         
  214.         For an octave higher, the ratio is 2.0:1.0 and
  215.         samples are skipped. For an octave lower, the
  216.         ration is 0.5:1.0 and samples are doubled.
  217.         For a musical interval of a perfect fifth higher,
  218.         the ration is 1.5:1.0, etc. 
  219.  
  220.         To make calculations based on a piano keyboard, the
  221.         TWELFTH_ROOT_OF_TWO is used as a multiplier to
  222.         find any pitch on the piano keyboard.  The number
  223.         of half-steps away a pitch is from MIDDLEC is
  224.         the power that the ratio is raised to. Thus,
  225.         the C-sharp above MIDDLEC has a ratio of
  226.         TWELFTH_ROOT_OF_TWO:1.0 with respect to MIDDLEC.
  227.         The D Natural above MIDDLE C is
  228.         TWELFTH_ROOT_OF_TWO ^ 2 : 1.0 with repect to
  229.         MIDDLEC and so forth.
  230.  
  231.         The output pitch is based on the sampled audio input 
  232.         only. So this the time over which the pitch is 
  233.         harmonized is discetized and only the audio input
  234.         buffer for that duration of time is used as the
  235.         sample input for the algorithm.  Consequently,
  236.         a continuous version of this algorithm (ala
  237.         glissando) would be less efficient to code.
  238.         
  239.  
  240. */
  241.     if(firsttime)    
  242.     {
  243.         for(j=0;j<MAXKEYS;j++)
  244.         index[j]=0.0;
  245.     firsttime = 0;
  246.     }
  247.         
  248.     for(i=0; i<buflen; i++) 
  249.     outbuf[i] = 0;
  250.  
  251.     for(j=0;j<MAXKEYS;j++)
  252.     {
  253.     if(key[j]>0) /* For each key in that is on in the
  254.                     known state of the keyboard, perform
  255.                     the operations necessary for sound. */
  256.     {    
  257.         ratio = powf(TWELFTH_ROOT_OF_2,
  258.                  (float)(key[j]-MIDDLEC));
  259.  
  260.            for(i=0; i<buflen; i++) 
  261.             {
  262.             outbuf[i] += inbuf[(int)index[j]%buflen]/MAXKEYS;
  263.             index[j] += ratio;
  264.             }
  265.     }
  266.     }
  267. }
  268.  
  269.  
  270. void audioexit(int exitval)        /* Gracefully exit the audio process */
  271. {
  272.     if (audioconfig != NULL)        ALfreeconfig(audioconfig);
  273.     if (audioout != NULL)           ALcloseport(audioout);
  274.     if (audioin != NULL)            ALcloseport(audioin);
  275.  
  276.     printf("audio closed down OK\n.");
  277.     audiodone = 1;
  278.     exit(exitval);
  279. }
  280.  
  281.  
  282. int main(int argc, char *argv[])
  283. {
  284. int whichkey;
  285. int arg;
  286.     /* set up signal handler for correct temrination */
  287.     signal(SIGINT,sigcat);
  288.     signal(SIGTERM,sigcat);
  289.  
  290.     /* Initialize our concept of keyboard state */
  291.     for(whichkey=0; whichkey<MAXKEYS; whichkey++)
  292.         key[whichkey] = -1;
  293.     whichkey = 0;
  294.  
  295.     if (argc == 1)
  296.     {
  297.         printf("usage: %s note <note2 note3 ...>\n",argv[0]);
  298.         printf("  where each note is an integer between %d and %d\n",
  299.             (-MIDDLEC),MIDDLEC);
  300.         exit (-1);
  301.     }
  302.  
  303.     printf("%d args \n",argc);
  304.     for(arg = 1; arg<argc; arg++)
  305.         if (whichkey < MAXKEYS)
  306.         {
  307.             printf("%d\n",MIDDLEC + atoi(argv[arg]));
  308.             key[whichkey++] = (MIDDLEC + atoi(argv[arg]));
  309.         }
  310.  
  311.     audioloop(&whichkey);
  312. }
  313.